package com.googlecode.jsqlconverter.filter;

import com.googlecode.jsqlconverter.annotation.ParameterName;
import com.googlecode.jsqlconverter.annotation.ServiceName;
import com.googlecode.jsqlconverter.definition.Name;
import com.googlecode.jsqlconverter.definition.create.index.CreateIndex;
import com.googlecode.jsqlconverter.definition.create.table.Column;
import com.googlecode.jsqlconverter.definition.create.table.CreateTable;
import com.googlecode.jsqlconverter.definition.create.table.constraint.CompoundForeignKeyConstraint;
import com.googlecode.jsqlconverter.definition.create.table.constraint.KeyConstraint;
import com.googlecode.jsqlconverter.definition.insert.InsertFromValues;
import com.googlecode.jsqlconverter.definition.truncate.table.Truncate;
import com.googlecode.jsqlconverter.filter.interfaces.TransformIndex;
import com.googlecode.jsqlconverter.filter.interfaces.TransformInsertFromValues;
import com.googlecode.jsqlconverter.filter.interfaces.TransformTable;
import com.googlecode.jsqlconverter.filter.interfaces.TransformTruncate;
import com.rits.cloning.Cloner;

@ServiceName("NameCaseFilter")
public class NameCaseFilter extends Filter implements TransformIndex, TransformInsertFromValues, TransformTable,
													  TransformTruncate
{
	private Case to;
	private Cloner cloner=new Cloner();
	
	public NameCaseFilter(
			@ParameterName("target") Case target )
	{
		to=target;
	}

	private String convert(String name) {
		switch (to) {
		case Upper: 
			return name.toUpperCase();
		case Lower:
			return name.toLowerCase();
		case Snake:
			return name.replaceAll("([a-z0-9])([A-Z])", "$1_$2").toLowerCase();
		case Camel:
			return snakeToCamel(name);
		}
		return name;
	}
	
	private String snakeToCamel(String name) {
		int snakeAt=-1;
		
		if (name.endsWith("_")) name=name.substring(0,name.length()-1);
		
		while ((snakeAt=name.indexOf('_'))>-1) {
			name=name.substring(0,snakeAt)+Character.toUpperCase(name.charAt(snakeAt+1))+name.substring(snakeAt+2);
		}
		return Character.toUpperCase(name.charAt(0))+name.substring(1);
	}

	private void convert(Name name) {
		name.setObjectName(convert(name.getObjectName()));
	}
	
	private void convert(Column c) {
		convert(c.getName());
		if (c.getForeignKeyConstraint()!=null) {
			convert(c.getForeignKeyConstraint().getTableName());
			convert(c.getForeignKeyConstraint().getColumnName());
		}
	}
	
	private void convert(CompoundForeignKeyConstraint ck) {
		convert(ck.getTableName());
		for (Name n : ck.getColumnNames())
			convert(n);
		for (Name n : ck.getReferenceColumns())
			convert(n);
	}
	
	@Override
	public Truncate transform(Truncate source) {
		Truncate ret=cloner.deepClone(source);
		convert(ret.getTableName());
		return ret;
	}

	@Override
	public CreateTable transform(CreateTable source) {
		CreateTable ret=cloner.deepClone(source);
		convert(ret.getName());
		for (Column c : ret.getColumns())
			convert(c);
		for (CompoundForeignKeyConstraint ck : ret.getCompoundForeignKeyConstraints())
			convert(ck);
		for (KeyConstraint kc : ret.getUniqueCompoundKeyConstraint())
			for (Name n : kc.getColumns())
				convert(n);
		if (ret.getPrimaryCompoundKeyConstraint()!=null)
			for (Name n : ret.getPrimaryCompoundKeyConstraint().getColumns())
				convert(n);
	
		return ret;
	}

	@Override
	public InsertFromValues transform(InsertFromValues source) {
		InsertFromValues ret=cloner.deepClone(source);
		convert(ret.getTableName());
		for (Column c : ret.getColumns())
			convert(c);
		return ret;
	}

	@Override
	public CreateIndex transform(CreateIndex source) {
		CreateIndex ret=cloner.deepClone(source);
		convert(ret.getIndexName());
		convert(ret.getTableName());
		for (Name c: ret.getColumns())
			convert(c);
		return ret;
	}
}
